//  GREKO Chess Engine
//  (c) 2002-2012 Vladimir Medvedev <vrm@bk.ru>
//  http://greko.110mb.com

//  position.h: position and game representation
//  modified: 05-Aug-2012

#pragma once

#include "bitboards.h"

extern EVAL VALUE[14];

inline int   Col(int f) { return (f % 8); }
inline int   Row(int f) { return (f / 8); }
inline COLOR GetColor(PIECE p) { return (p & 1); }
inline COLOR Opp(COLOR side) { return (side ^ 1); }

class Move
{
public:

  Move() : _data(0) {}
  Move(U32 x) : _data(x) {}
  Move(FLD from, FLD to, PIECE piece) : 
    _data(U32(from) | 
    (U32(to) << 6) | 
    (U32(piece) << 12)) {}
  Move(FLD from, FLD to, PIECE piece, PIECE captured) : 
    _data(U32(from) | 
    (U32(to) << 6) | 
    (U32(piece) << 12) |
    (U32(captured) << 16)) {}
  Move(FLD from, FLD to, PIECE piece, PIECE captured, PIECE promotion) : 
    _data(U32(from) | 
    (U32(to) << 6) | 
    (U32(piece) << 12) |
    (U32(captured) << 16) |
    (U32(promotion) << 20)) {}

  FLD From() const { return _data & 0x3f; }
  FLD To() const { return (_data >> 6) & 0x3f; }
  FLD Piece() const { return (_data >> 12) & 0x0f; }
  FLD Captured() const { return (_data >> 16) & 0x0f; }
  FLD Promotion() const { return (_data >> 20) & 0x0f; }

  operator U32() const { return _data; }

private:

  U32 _data;
};

class Position
{
public:

  PIECE operator[] (int f) const { return _board[f]; }

  U64   Bits(PIECE p) const { return _bits[p]; }
  U64   BitsAll(COLOR side) const { return _bitsAll[side]; }
  U64   BitsAll() const { return _bitsAll[WHITE] | _bitsAll[BLACK]; }
  U8    Castlings() const { return _castlings; }
  int   Count(PIECE p) const { return _count[p]; }
  FLD   Ep() const { return _ep; }
  int   Fifty() const { return _fifty; }  
  U64   GetAttacks(FLD to, COLOR side, U64 occ) const;
  int   GetRepetitions() const;
  std::string Fen() const;
  U64   Hash() const { return _hash ^ _castlings ^ _ep; }
  static void InitHashNumbers();
  bool  InCheck() const { return IsAttacked(King(Side()), Side() ^ 1); }
  bool  IsAttacked(FLD to, COLOR side) const;
  bool  IsGameOver(std::string& message);
  FLD   King(COLOR side) const { return _Kings[side]; }
  EVAL  Material(COLOR side) const { return _material[side]; }
  int   MatIndex(COLOR side) const { return _matIndex[side]; }
  U32   PawnHash() const { return _hashPawn; }
  int   Ply() const { return _ply; }
  void  Print() const;
  bool  SetFen(const std::string& fen);
  void  SetInitial() { SetFen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"); }
  COLOR Side() const { return _side; }

  bool MakeMove(Move mv);
  void MakeNullMove();
  void Mirror();
  void UnmakeMove();
  void UnmakeNullMove();

private:

  void Clear();
  void Put(FLD f, PIECE p);
  void Remove(FLD f);

  U64   _bits[14];
  U64   _bitsAll[2];
  PIECE _board[64];
  U8    _castlings;
  int   _count[14];
  FLD   _ep;
  int   _fifty;
  U64   _hash;
  U32   _hashPawn;
  FLD   _Kings[2];
  EVAL  _material[2];
  int   _matIndex[2];
  int   _ply;
  COLOR _side;

  enum { MAX_UNDO = 1024 };
  struct Undo
  {
    U8   _castlings;
    FLD  _ep;
    int  _fifty;
    U64  _hash;
    Move _mv;
  };
  Undo _undos[MAX_UNDO];
  int _undoSize;

  static U64 s_hashSide[2];
  static U64 s_hash[64][14];
  static U32 s_hashPawn[64][14];
};

static const int DELTA[14] =
{ 0, 0, 0, 0, 3, 3, 3, 3, 5, 5, 10, 10, 0, 0 };

inline void Position::Put(FLD f, PIECE p)
{
  assert(f <= H1);
  assert(p != NOPIECE);
  assert(_board[f] == NOPIECE);

  _board[f] = p;
  _bits[p] ^= Bitboard::Single(f);
  _bitsAll[GetColor(p)] ^= Bitboard::Single(f);
  _hash ^= s_hash[f][p];
  _hashPawn ^= s_hashPawn[f][p];

  COLOR side = GetColor(p);
  ++_count[p];
  _matIndex[side] += DELTA[p];
  _material[side] += VALUE[p];
}

inline void Position::Remove(FLD f)
{
  assert(f <= H1);
  PIECE p = _board[f];
  assert(p != NOPIECE);

  _board[f] = NOPIECE;
  _bits[p] ^= Bitboard::Single(f);
  _bitsAll[GetColor(p)] ^= Bitboard::Single(f);
  _hash ^= s_hash[f][p];
  _hashPawn ^= s_hashPawn[f][p];

  COLOR side = GetColor(p);
  --_count[p];
  _matIndex[side] -= DELTA[p];
  _material[side] -= VALUE[p];
}

const FLD FLIP[64] =
{
  A1, B1, C1, D1, E1, F1, G1, H1,
  A2, B2, C2, D2, E2, F2, G2, H2,
  A3, B3, C3, D3, E3, F3, G3, H3,
  A4, B4, C4, D4, E4, F4, G4, H4,
  A5, B5, C5, D5, E5, F5, G5, H5,
  A6, B6, C6, D6, E6, F6, G6, H6,
  A7, B7, C7, D7, E7, F7, G7, H7,
  A8, B8, C8, D8, E8, F8, G8, H8
};
